package com.rainbow.support.mybatis.handler;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.rainbow.common.core.toolkit.ContextHolder;
import com.rainbow.support.mybatis.entity.BaseEntity;
import com.rainbow.support.mybatis.properties.MybatisExtraProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Objects;

/**
 * MyBatis Plus 元数据处理类
 * 用于自动 注入 id, createdTime, lastModifiedTime, createdBy, lastModifiedBy 等字段
 * @author 付为地
 * @since 2021/09/26
 */
@Slf4j
@RequiredArgsConstructor
public class MyBatisMetaObjectHandler implements MetaObjectHandler {

    private final MybatisExtraProperties properties;

    /**
     * 注意：不支持 复合主键 自动注入！！
     * <p>
     * 所有的继承了Entity、SuperEntity的实体，在insert时，
     * id： id为空时， 通过IdGenerate生成唯一ID， 不为空则使用传递进来的id
     * createdBy, lastModifiedBy: 自动赋予 当前线程上的登录人id
     * createdTime, lastModifiedTime: 自动赋予 服务器的当前时间
     * <p>
     * 未继承任何父类的实体，且主键标注了 @TableId(value = "xxx", type = IdType.INPUT) 自动注入 主键
     * 主键的字段名称任意
     * mysql时间戳到微秒(需要将Instant转换成微秒一下)
     * pgsql时间戳到纳秒
     *
     * @param metaObject metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        //当前线程中的用户id
        final String currentId =ContextHolder.getUserId();
        final Object originalObject = metaObject.getOriginalObject();
        final Instant now= Objects.equals(DbType.MYSQL,properties.getDbType())?Instant.now().truncatedTo(ChronoUnit.MICROS):Instant.now();
        if (originalObject instanceof BaseEntity) {
            BaseEntity currentEntity = (BaseEntity) originalObject;
            //添加时,同步创建时间
            this.setFieldValByName(BaseEntity.CREATOR,ObjectUtils.isEmpty(currentEntity.getCreator())?currentId:currentEntity.getCreator(), metaObject);
            this.setFieldValByName(BaseEntity.CREATE_TIME, ObjectUtils.isEmpty(currentEntity.getCreateTime())?now:currentEntity.getCreateTime(), metaObject);
            this.setFieldValByName(BaseEntity.LOGIC_DELETE, false, metaObject);
            /*同步更新时间
            this.setFieldValByName(BaseEntity.UPDATER, currentId, metaObject);
            this.setFieldValByName(BaseEntity.UPDATE_TIME, now, metaObject);*/
        } else {
            final ObjectWrapper wrapper = metaObject.getObjectWrapper();
            if (wrapper.hasGetter(BaseEntity.CREATOR)) {
                String creator=Objects.toString(wrapper.get(new PropertyTokenizer(BaseEntity.CREATOR)),currentId);
                this.setFieldValByName(BaseEntity.CREATOR, creator, metaObject);
            }
            if (wrapper.hasGetter(BaseEntity.CREATE_TIME)) {
                Object preCreatorTime=wrapper.get(new PropertyTokenizer(BaseEntity.CREATE_TIME));
                this.setFieldValByName(BaseEntity.CREATE_TIME, ObjectUtils.isEmpty(preCreatorTime)?now:preCreatorTime, metaObject);
            }
            if (wrapper.hasGetter(BaseEntity.LOGIC_DELETE)) {
                this.setFieldValByName(BaseEntity.LOGIC_DELETE, false, metaObject);
            }
            //新增时,同步更新时间
            /*if (wrapper.hasGetter(BaseEntity.UPDATER)) {
                Object preUpdater=wrapper.get(new PropertyTokenizer(BaseEntity.UPDATER));
                this.setFieldValByName(BaseEntity.UPDATER, ObjectUtils.isEmpty(preUpdater)?currentId:preUpdater, metaObject);
            }
            if (wrapper.hasGetter(BaseEntity.UPDATE_TIME)) {
                Object preUpdateTime=wrapper.get(new PropertyTokenizer(BaseEntity.UPDATE_TIME));
                this.setFieldValByName(BaseEntity.UPDATE_TIME, ObjectUtils.isEmpty(preUpdateTime)?now:preUpdateTime, metaObject);
            }*/
        }
    }

    /**
     * 所有的继承了Entity、SuperEntity的实体，在update时，
     * lastModifiedBy: 自动赋予 当前线程上的登录人id
     * lastModifiedTime: 自动赋予 服务器的当前时间
     * @param metaObject metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        //当前线程中的用户id
        final String currentId =ContextHolder.getUserId();
        final Object originalObject = metaObject.getOriginalObject();
        final Instant now= Objects.equals(DbType.MYSQL,properties.getDbType())?Instant.now().truncatedTo(ChronoUnit.MICROS):Instant.now();
        if (originalObject instanceof BaseEntity) {
            BaseEntity currentEntity = (BaseEntity) originalObject;
            this.setFieldValByName(BaseEntity.UPDATER, ObjectUtils.isEmpty(currentEntity.getUpdater())?currentId:currentEntity.getUpdater(), metaObject);
            this.setFieldValByName(BaseEntity.UPDATE_TIME,ObjectUtils.isEmpty(currentEntity.getUpdateTime())?now:currentEntity.getUpdateTime(), metaObject);
        } else {
            final ObjectWrapper wrapper = metaObject.getObjectWrapper();
            if (wrapper.hasGetter(BaseEntity.UPDATER)) {
                Object preUpdater=wrapper.get(new PropertyTokenizer(BaseEntity.UPDATER));
                this.setFieldValByName(BaseEntity.UPDATER, ObjectUtils.isEmpty(preUpdater)?currentId:preUpdater, metaObject);
            }
            if (wrapper.hasGetter(BaseEntity.UPDATE_TIME)) {
                Object preUpdateTime=wrapper.get(new PropertyTokenizer(BaseEntity.UPDATE_TIME));
                this.setFieldValByName(BaseEntity.UPDATE_TIME, ObjectUtils.isEmpty(preUpdateTime)?now:preUpdateTime, metaObject);
            }
        }
    }
}
